import com.jogamp.opengl.util.FPSAnimator;
import com.jogamp.opengl.util.gl2.GLUT;

import javax.media.opengl.*;
import javax.media.opengl.awt.GLCanvas;
import javax.media.opengl.fixedfunc.GLLightingFunc;
import javax.media.opengl.fixedfunc.GLMatrixFunc;
import javax.media.opengl.glu.GLU;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

/**
 * ported to JOGL 2.0 by Julien Gouesse (http://tuer.sourceforge.net)
 */
public class World implements GLEventListener, KeyListener {
    float rotateT = 0.0f;
    static GLU glu = new GLU();
    static GLCanvas canvas = new GLCanvas();
    static Frame frame = new Frame("Jogl 3D Shape/Rotation");
    static FPSAnimator animator = new FPSAnimator(canvas, 60);
    private boolean isFullScreen = true;

    private Camera camera = new Camera();
    private Ground ground = new Ground();
    private Light light = new Light();

    public void display(GLAutoDrawable gLDrawable) {
        final GL2 gl = gLDrawable.getGL().getGL2();
        final GLUT glut = new GLUT();

        gl.glClear(GL.GL_COLOR_BUFFER_BIT);
        gl.glClear(GL.GL_DEPTH_BUFFER_BIT);
        gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
        gl.glLoadIdentity();

        camera.render(gl, glu);
        light.render(gl);
        ground.render(gl);

        //Sphere to test rotation stuff with.
        gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_SPECULAR, new float[]{1.0f, 1.0f, 1.0f, 1.0f}, 0);
        gl.glMateriali(GL.GL_FRONT, GLLightingFunc.GL_SHININESS, 50);

        gl.glPushMatrix();
        gl.glTranslatef(0.0f, 0.5f, -3.0f);
        gl.glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
        gl.glColor3f(0.0f, 0.0f, 1.0f);

        glut.glutSolidSphere(0.5, 50, 50);
        gl.glPopMatrix();

        gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_SPECULAR, new float[]{0.0f, 0.0f, 0.0f, 0.0f}, 0);
        gl.glMateriali(GL.GL_FRONT, GLLightingFunc.GL_SHININESS, 0);
        gl.glPushMatrix();
        gl.glTranslatef(0.0f, 0.5f, -5.0f);
        gl.glColor3f(1.0f, 0.0f, 0.0f);
        glut.glutSolidCube(1);
        gl.glPopMatrix();


        //these two need normals for lighting. Normal needs to point towards camera...
        gl.glPushMatrix();
        gl.glMultMatrixf(camera.getBillboardMatrix(), 0);
        gl.glPushMatrix();
        gl.glTranslatef(-6.0f, 4.0f, -3.0f);
        gl.glBegin(GL.GL_TRIANGLE_STRIP);
        gl.glColor3f(0.75f, 0.0f, 1.0f);
        gl.glVertex3f(-1.0f, -1.0f, 0.0f);
        gl.glVertex3f(1.0f, -1.0f, 0.0f);
        gl.glVertex3f(-1.0f, 1.0f, 0.0f);
        gl.glVertex3f(1.0f, 1.0f, 0.0f);
        gl.glEnd();

        gl.glTranslatef(0.0f, 3.0f, -5.0f);
        gl.glBegin(GL.GL_TRIANGLE_STRIP);
        gl.glColor3f(0.0f, 1.0f, 0.0f);
        gl.glVertex3f(0.0f, 0.5f, 0.0f);
        gl.glVertex3f(-0.5f, -0.5f, 0.0f);
        gl.glVertex3f(0.5f, -0.5f, 0.0f);
        gl.glEnd();

        gl.glPopMatrix();
        gl.glPopMatrix();
    }

    public void displayChanged(GLAutoDrawable gLDrawable, boolean modeChanged, boolean deviceChanged) {
    }

    public void init(GLAutoDrawable gLDrawable) {
        GL2 gl = gLDrawable.getGL().getGL2();

        gl.glShadeModel(GLLightingFunc.GL_SMOOTH);
        gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        gl.glClearDepth(1.0f);

        gl.glCullFace(GL.GL_BACK);
        gl.glEnable(GL.GL_DEPTH_TEST);
        gl.glDepthFunc(GL.GL_LEQUAL);
        gl.glHint(GL2ES1.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);
        gl.glEnable(GL.GL_CULL_FACE);
        gl.glEnable(GL2ES1.GL_FOG);
        gl.glFogfv(GL2ES1.GL_FOG_COLOR, new float[]{0.0f, 0.0f, 0.0f, 1.0f}, 0);
        gl.glFogf(GL2ES1.GL_FOG_START, 5.0f);
        gl.glFogf(GL2ES1.GL_FOG_END, 100.0f);
        gl.glFogi(GL2ES1.GL_FOG_MODE, GL.GL_LINEAR);

        ((Component) gLDrawable).addKeyListener(this);
    }

    public void reshape(GLAutoDrawable gLDrawable, int x, int y, int width, int height) {
        GL2 gl = gLDrawable.getGL().getGL2();
        if (height <= 0) {
            height = 1;
        }
        float h = (float) width / (float) height;
        gl.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
        gl.glLoadIdentity();
        glu.gluPerspective(60.0f, h, 0.1, 1000.0);
        gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
        gl.glLoadIdentity();
    }

    public void keyPressed(KeyEvent e) {
        int code = e.getKeyCode();
        switch(code){
            case KeyEvent.VK_ESCAPE:
                exit();
                break;
            case KeyEvent.VK_F:
                isFullScreen = !isFullScreen;
                if(isFullScreen)
                    frame.setExtendedState(Frame.MAXIMIZED_BOTH);
                else
                    frame.setExtendedState(Frame.NORMAL);
                break;
            case KeyEvent.VK_DOWN:
                camera.setxRotate(camera.getxRotate() - 5.0f);
                break;
            case KeyEvent.VK_UP:
                camera.setxRotate(camera.getxRotate() + 5.0f);
                break;
            case KeyEvent.VK_RIGHT:
                camera.setyRotate(camera.getyRotate() - 5.0f);
                break;
            case KeyEvent.VK_LEFT:
                camera.setyRotate(camera.getyRotate() + 5.0f);
                break;
            case KeyEvent.VK_1:
                camera.setDistance(camera.getDistance() - 1.0f);
                break;
            case KeyEvent.VK_2:
                camera.setDistance(camera.getDistance() + 1.0f);
                break;
            case KeyEvent.VK_SPACE:
                camera.reset();
                break;
        }
    }

    public void keyReleased(KeyEvent e) {
    }

    public void keyTyped(KeyEvent e) {
    }

    public static void exit() {
        animator.stop();
        frame.dispose();
        System.exit(0);
    }

    public static void main(String[] args) {

        canvas.addGLEventListener(new World());
        frame.add(canvas);
        frame.setSize(1280, 768);
        frame.setUndecorated(true);
        frame.setExtendedState(Frame.MAXIMIZED_BOTH);
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                exit();
            }
        });
        frame.setVisible(true);
        canvas.requestFocus();
        animator.start();
    }

    public void dispose(GLAutoDrawable gLDrawable) {
        // do nothing
    }
}